// Copyright (c) 2024. Tony Robalik.
// SPDX-License-Identifier: Apache-2.0
package com.autonomousapps.internal.artifacts

import org.gradle.api.Named
import org.gradle.api.NamedDomainObjectProvider
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration

/**
 * Used for resolving custom artifacts in an aggregating project (often the "root" project), from producing projects
 * (often all or a subset of the subprojects ina build). Only for inter-project publishing and resolving (e.g., _not_
 * for publishing to Artifactory). See also [Publisher].
 *
 * Represents a set of tightly coupled [Configuration]s:
 * * A "dependency scope" configuration ([declarable]).
 * * A "resolvable" configuration ([internal]).
 * * A "consumable" configuration ([Publisher.external]).
 *
 * Dependencies are _declared_ on [declarable], and resolved within a project via [internal]. Custom artifacts (e.g.,
 * not jars), generated by tasks, are published via [Publisher.publish], which should be used on dependency
 * (artifact-producing) projects.
 *
 * Gradle uses [attributes][Attr] to wire the consumer project's [internal] (resolvable) configuration to the producer
 * project's [Publisher.external] (consumable) configuration, which is itself configured via [Publisher.publish].
 *
 * @see <a href="https://docs.gradle.org/current/userguide/cross_project_publications.html#sec:variant-aware-sharing">Variant-aware sharing of artifacts between projects</a>
 * @see <a href="https://dev.to/autonomousapps/configuration-roles-and-the-blogging-industrial-complex-21mn">Gradle configuration roles</a>
 */
internal class Resolver<T : Named>(
  project: Project,
  declarableName: String,
  attr: Attr<T>,
) {

  internal companion object {
    /** Convenience function for creating a [Resolver] for inter-project resolving of [DagpArtifacts]. */
    fun interProjectResolver(
      project: Project,
      artifact: DagpArtifacts.Kind,
    ): Resolver<DagpArtifacts> {
      return Resolver(
        project,
        artifact.declarableName,
        Attr(DagpArtifacts.DAGP_ARTIFACTS_ATTRIBUTE, artifact.artifactName)
      )
    }
  }

  // Following the naming pattern established by the Java Library plugin. See
  // https://docs.gradle.org/current/userguide/java_library_plugin.html#sec:java_library_configurations_graph
  private val internalName = "${declarableName}Classpath"

  /** Dependencies are declared on this configuration */
  val declarable: Configuration = project.dependencyScopeConfiguration(declarableName).get()

  /**
   * The plugin will resolve dependencies against this internal configuration, which extends from
   * the declared dependencies.
   */
  val internal: NamedDomainObjectProvider<out Configuration> =
    project.resolvableConfiguration(internalName, declarable) {
      // This attribute is identical to what is set on the external/consumable configuration
      attributes {
        attribute(
          attr.attribute,
          project.objects.named(attr.attribute.type, attr.attributeName)
        )
        attribute(
          DagpArtifacts.CATEGORY_ATTRIBUTE,
          DagpArtifacts.category(project.objects)
        )
      }
    }
}
